﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WcfMasterSlaveElectionService;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace WcfActivePassiveWorkerRole
{
    /// <summary>
    /// Implementation of IEndpointProvider.
    /// the endpoint provider should know all instances to select a master from. This implemetation is
    /// specific for Windows Azure.
    /// </summary>
    public class AzureEndpointProvider : IEndpointProvider
    {
        #region Fields

        /// <summary>
        /// Name of the endpoint
        /// </summary>
        private const string ElectionServiceEndpointName = "MasterElectionService";

        /// <summary>
        /// Name of the role instance
        /// </summary>
        private readonly string _rolename;

        /// <summary>
        /// Basic logging facility
        /// </summary>
        private readonly ILog _logClient;

        /// <summary>
        /// List with all known instances
        /// </summary>
        Dictionary<string, string> _electionServices = null;
        #endregion

        public AzureEndpointProvider(ILog logClient)
        {
            _rolename = RoleEnvironment.CurrentRoleInstance.Role.Name;
            _logClient = logClient;
        }

        #region IEndpointProvider
        /// <summary>
        /// Gets all known endpoints, including its own endpoint
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> GetCurrentEndpoints()
        {
            return _electionServices;
        }

        /// <summary>
        /// Gets the URI for an instance with given id
        /// </summary>
        /// <param name="roleInstanceId"></param>
        /// <returns></returns>
        public string GetUriForRoleIntance(string roleInstanceId)
        {
            RoleInstance roleInstance = GetRoleInstance(roleInstanceId);
            return GetUriForRoleIntance(roleInstance);
        }

        /// <summary>
        /// Gets the endpoint of the instance identified by roleinstanceId
        /// </summary>
        /// <param name="roleInstanceId"></param>
        /// <returns>The InstanceEnpoint. When the instance is not found, null is returned</returns>
        public InstanceEndpoint GetEndPointOfRoleIntance(string roleInstanceId)
        {
            InstanceEndpoint instanceEndpoint = null;
            foreach (var roleInstance in RoleEnvironment.Roles[_rolename].Instances)
            {
                if (roleInstanceId == roleInstance.Id)
                {
                    instanceEndpoint = new InstanceEndpoint();
                    RoleInstanceEndpoint endpoint = roleInstance.InstanceEndpoints[ElectionServiceEndpointName];
                    instanceEndpoint.Id = roleInstance.Id;
                    instanceEndpoint.IpEndpoint = endpoint.IPEndpoint;
                    instanceEndpoint.Uri = BuildUri(endpoint);
                    break;
                }
            }

            return instanceEndpoint;
        }

        internal RoleInstance GetRoleInstance(string roleInstanceId)
        {
            RoleInstance roleInstance = (from ri in RoleEnvironment.Roles[_rolename].Instances where ri.Id == roleInstanceId select ri).Single();
            return roleInstance;
        }

        /// <summary>
        /// Gets the name of the endpoint of the MasterSlaveElectionService
        /// </summary>
        public string EndpointName
        {
            get { return ElectionServiceEndpointName; }
        }

        /// <summary>
        /// Gets the name of the RoleInstance
        /// </summary>
        public string RoleName
        {
            get { return this._rolename; }
        }
        #endregion

        /// <summary>
        /// Fills the list of services participating in the master/slave election
        /// </summary>
        private void FillElectionServicesList()
        {
            if (_electionServices == null)
            {
                _electionServices = new Dictionary<string, string>();
            }

            _electionServices.Clear();

            // Getting the MasterSlaveElectionService endpoints of all role instances
            foreach (var roleInstance in RoleEnvironment.Roles[_rolename].Instances)
            {
                var uri = GetUriForRoleIntance(roleInstance);

                _logClient.LogMessage("RoleInstance {0}, endpoint URI: {1}", roleInstance.Id, uri);

                // Add a client to the dictionary by creating a new client based on the URI identified by the endpoint ID
                // This does not include the endpoint of the current role instance
                _electionServices.Add(roleInstance.Id, uri);
            }
        }

        private string GetUriForRoleIntance(RoleInstance roleInstance)
        {
            RoleInstanceEndpoint endpoint = roleInstance.InstanceEndpoints[ElectionServiceEndpointName];
            var uri = BuildUri(endpoint);
            return uri;
        }

        /// <summary>
        /// Builds the URI specific to the given enpoint
        /// </summary>
        /// <param name="endpoint"></param>
        /// <returns></returns>
        private string BuildUri(RoleInstanceEndpoint endpoint)
        {
            return String.Format("net.tcp://{0}:{1}/{2}",
                endpoint.IPEndpoint.Address,
                endpoint.IPEndpoint.Port,
                ElectionServiceEndpointName);
        }

        /// <summary>
        /// Handles the ConfigurationChanged event. When the environment has changed, the number of instances may
        /// have changed. Referesh the list of instances
        /// </summary>
        public void OnConfigurationChanged()
        {
            FillElectionServicesList();
        }
    }
}
